home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
...taking it to the Macs!
/
...taking it to the Macs!.iso
/
Extras
/
ActiveX Mac SDK
/
ActiveX SDK
/
Container Common
/
xbinding.cpp
< prev
next >
Wrap
Text File
|
1997-01-03
|
13KB
|
519 lines
//
// XBINDING.CPP
//
// Copyright (C) Microsoft Corporation, 1996
//
// Implements the asynchronous binding model on top of the Netscape plugin
// APIs.
//
#include "headers.h"
// Default FORMATETC that is passed to IBindStatusCallback::OnDataAvailable.
FORMATETC g_NullFormatEtc = {
CF_NULL,
NULL,
DVASPECT_CONTENT,
-1,
TYMED_NULL
};
OLECHAR g_szBSCBHolder[] = "_BSCB_Holder_";
CXBinding::~CXBinding()
{
if (m_pbsc != NULL)
m_pbsc->Release();
}
//
// CXBinding::Bind
//
HRESULT
CXBinding::Bind(LPOLESTR pszURL, LPBINDSTATUSCALLBACK pbsc, unsigned long BindType)
{
HRESULT hr;
DWORD bindf;
BINDINFO bindinfo;
CNetscapeStreamNotify *pBindingNotify;
// Query the client for the type of binding operation they want.
memset(&bindinfo, 0, sizeof(bindinfo));
bindinfo.cbSize = sizeof(bindinfo);
pbsc->GetBindInfo(&bindf, &bindinfo);
// BUGBUG: What exactly are we supposed to do with this?
if (bindinfo.szExtraInfo != NULL)
CoTaskMemFree(bindinfo.szExtraInfo);
// Verify that the client is asking for an async, non-blocking, push-data
// read binding.
if (!(bindf & BINDF_ASYNCHRONOUS) || !(bindf & BINDF_ASYNCSTORAGE) ||
((bindf & BINDF_PULLDATA) && (BindType == TYMED_FILE || BindType == TYMED_FSP))
|| (bindinfo.dwBindVerb != BINDVERB_GET))
return ResultFromScode(E_FAIL);
// Notify the callback that we're about to start binding.
hr = pbsc->OnStartBinding(BSCO_ALLONIBSC, (LPBINDING) this);
if (m_fAborted)
hr = ResultFromScode(E_ABORT);
if (SUCCEEDED(hr))
{
// Create the appopriate stream notification object.
if (BindType == TYMED_FILE || BindType == TYMED_FSP)
{
pBindingNotify = new CBindingFileStreamNotify(this, BindType);
}
else if(bindf & BINDF_PULLDATA)
{
pBindingNotify = new CBindingPullStreamNotify(this);
}
else
{
pBindingNotify = new CBindingMemoryStreamNotify(this);
}
if ((pBindingNotify == NULL) ||
((pBindingNotify->m_pszURL = OleStrdup(pszURL)) == NULL))
{
hr = ResultFromScode(E_OUTOFMEMORY);
}
#ifdef PLUGIN_ADAPTER
else if (NPN_GetURL((NPP) m_pSite->m_ClientInstance,
pBindingNotify->m_pszURL, NULL) != NPERR_NO_ERROR)
#elif defined(URLMON_DLL)
else if (AXGetUrl( pBindingNotify->m_pszURL, pBindingNotify,
BindType == TYMED_FILE || BindType == TYMED_FSP) != NPERR_NO_ERROR)
#else
else if (AXE_GetURL((NPP) m_pSite->m_ClientInstance,
pBindingNotify->m_pszURL, NULL, pBindingNotify) != NPERR_NO_ERROR)
#endif
{
delete pBindingNotify;
pBindingNotify = NULL;
hr = ResultFromScode(E_FAIL);
} else {
#ifdef PLUGIN_ADAPTER
// Link the notification object to the list of objects waiting to
// be bound.
pBindingNotify->m_pNextUnattached = m_pSite->m_pUnattachedList;
m_pSite->m_pUnattachedList = pBindingNotify;
#endif
m_pbsc = pbsc;
m_pbsc->AddRef();
// Result indicates that we're in the process of binding, not an error.
hr = ResultFromScode(E_PENDING);
}
}
// If for any reason we're going to return E_PENDING, we need to call
// OnStopBinding and notify it of the error.
if (hr != ResultFromScode(E_PENDING)) {
pbsc->OnStopBinding(hr, NULL);
}
return hr;
}
//
// CXBinding::IUnknown::QueryInterface
//
// Returns a pointer to the specified interface on a component to which a
// client currently holds an interface pointer.
//
STDMETHODIMP
CXBinding::QueryInterface(REFIID riid, LPVOID *ppvObj)
{
HRESULT hr;
LPVOID pv;
if (riid == IID_IUnknown || riid == IID_IBinding) {
pv = (LPVOID)(LPBINDCTX) this;
++m_cRef;
hr = ResultFromScode(S_OK);
} else {
pv = NULL;
hr = ResultFromScode(E_NOINTERFACE);
}
*ppvObj = pv;
return hr;
}
//
// CXBinding::IUnknown::AddRef
//
// Increments the reference count for the calling interface.
//
STDMETHODIMP_(ULONG)
CXBinding::AddRef(void)
{
return ++m_cRef;
}
//
// CXBinding::IUnknown::Release
//
// Decrements the reference count for the calling interface on a object. If
// the reference count on the object falls to zero, the object is freed.
//
STDMETHODIMP_(ULONG)
CXBinding::Release(void)
{
if (--m_cRef != 0)
return m_cRef;
delete this;
return 0;
}
//
// CXBinding::IBinding::Abort
//
// Permanently aborts the bind operation.
//
STDMETHODIMP
CXBinding::Abort(void)
{
HRESULT hr;
// The binding operation will be aborted inside the Xxx::OnWrite method.
// As documented in the "Asynchronous Moniker" spec, the client may
// continue to receive callback notifications, so we can do this at our
// earliest convenience.
if (!m_fAborted) {
m_fAborted = TRUE;
hr = ResultFromScode(S_OK);
} else {
hr = ResultFromScode(S_FALSE);
}
return hr;
}
//
// CXBinding::IBinding::Suspend
//
// Suspends the bind operation until resumed by a call to IBinding::Resume or
// aborted by a call to IBinding::Abort.
//
STDMETHODIMP
CXBinding::Suspend(void)
{
// Suspend/resume not supported.
return ResultFromScode(E_NOTIMPL);
}
//
// CXBinding::IBinding::Resume
//
// Resumes a binding operation that was suspended by a call to
// IBinding::Suspend.
//
STDMETHODIMP
CXBinding::Resume(void)
{
// Suspend/resume not supported.
return ResultFromScode(E_NOTIMPL);
}
//
// CXBinding::IBinding::SetPriority
//
// Establishes the priority for the bind operation.
//
STDMETHODIMP
CXBinding::SetPriority(LONG nPriority)
{
#pragma unused (nPriority)
// Priority cannot be controlled.
return ResultFromScode(E_NOTIMPL);
}
//
// CXBinding::IBinding::GetPriority
//
// Retrieves the current priority of this bind operation.
//
STDMETHODIMP
CXBinding::GetPriority(LONG *pnPriority)
{
// Priority cannot be controlled.
*pnPriority = 0; // THREAD_PRIORITY_NORMAL.
return ResultFromScode(E_NOTIMPL);
}
//
// CXBinding::IBinding::GetBindResult
//
// Queries the protocol-specific outcome of a bind operation, typically used
// during IBindStatusCallback::OnStopBinding.
//
STDMETHODIMP
CXBinding::GetBindResult(LPCLSID pclsidProtocol, LPDWORD pdwResult, LPOLESTR
*pszResult, LPDWORD pdwReserved)
{
#pragma unused (pclsidProtocol, pdwReserved)
// We don't have any protocol-specific code, so there's nothing useful to
// return.
*pdwResult = 0;
*pszResult = NULL;
return ResultFromScode(S_OK);
}
//
// CBindingMemoryStreamNotify::OnWrite
//
// Extends the default memory stream implementation by notifying the client of
// data availability.
//
int32
CBindingMemoryStreamNotify::OnWrite(NPStream *stream, int32 offset, int32 len,
void *buffer)
{
int32 BytesWritten;
ULONG BindStatus;
DWORD fBSCF;
// Tag the stream such that requests for unavailable data made against our
// IStream return E_PENDING.
m_fAsyncStreamInProgress = TRUE;
BytesWritten = CMemoryOleStreamNotify::OnWrite(stream, offset, len, buffer);
if (BytesWritten > 0) {
if (!m_fDeliveredFirstNotify) {
m_fDeliveredFirstNotify = TRUE;
BindStatus = BINDSTATUS_BEGINDOWNLOADDATA;
fBSCF = BSCF_FIRSTDATANOTIFICATION;
} else {
BindStatus = BINDSTATUS_DOWNLOADINGDATA;
fBSCF = BSCF_INTERMEDIATEDATANOTIFICATION;
}
m_pBinding->m_pbsc->OnProgress((ULONG) m_StreamLength,
(ULONG) m_StreamLength, BindStatus, m_pszURL);
mStorageMedium.tymed = TYMED_ISTREAM;
mStorageMedium.pstm = (LPSTREAM) this;
mStorageMedium.pUnkForRelease = (LPUNKNOWN) this;
AddRef(); // account for mStorageMedium reference
m_pBinding->m_pbsc->OnDataAvailable(fBSCF, BytesWritten,
&g_NullFormatEtc, &mStorageMedium);
// If the client called IBinding::Abort, return an error so as to
// force the stream to terminate.
if (m_pBinding->m_fAborted)
BytesWritten = -1;
}
// If BytesWritten is negative, then the stream will be terminated. The
// client will be notified of the error in the OnDestroyStream method.
return BytesWritten;
}
//
// CBindingMemoryStreamNotify::OnDestroyStream
//
// Extends the default memory stream implementation by notifying the client
// that the binding operation is complete.
//
NPError
CBindingMemoryStreamNotify::OnDestroyStream(NPStream *stream, NPError reason)
{
#pragma unused (stream)
HRESULT hr;
m_fAsyncStreamInProgress = FALSE;
// You would think Netscape would pass the right reason code to us, but it
// always returns a zero on success, so maybe they want NPERR_NO_ERROR???
if ((reason == NPERR_NO_ERROR || reason == NPRES_DONE) &&
!m_pBinding->m_fAborted) {
// Send the final progress notification.
m_pBinding->m_pbsc->OnProgress((ULONG) m_StreamLength,
(ULONG) m_StreamLength, BINDSTATUS_ENDDOWNLOADDATA, m_pszURL);
// Although the data stream hasn't changed, we need to send the last
// data notification.
mStorageMedium.tymed = TYMED_ISTREAM;
mStorageMedium.pstm = (LPSTREAM) this;
mStorageMedium.pUnkForRelease = (LPUNKNOWN) this;
m_pBinding->m_pbsc->OnDataAvailable(BSCF_LASTDATANOTIFICATION,
m_StreamLength, &g_NullFormatEtc, &mStorageMedium);
hr = ResultFromScode(S_OK);
} else {
hr = ResultFromScode(E_ABORT);
}
// Notify the callback that the binding has stopped. Plus, we're through
// with the binding object, so we're ready to throw it away. The client
// is supposed to release the pointer in the OnStopBinding method, so we'll
// make sure we don't leak anything by just deleting the binding.
m_pBinding->m_pbsc->OnStopBinding(hr, NULL);
delete m_pBinding;
m_pBinding = NULL;
// We're through with this binding callback object. The user may be
// holding on to our stream object though, so we must pay attention to our
// reference count.
this->Release();
return NPERR_NO_ERROR;
}
//
// CBindingFileStreamNotify::IUnknown::QueryInterface
//
STDMETHODIMP
CBindingFileStreamNotify::QueryInterface(REFIID riid, LPVOID *ppvObj)
{
HRESULT hr;
LPVOID pv;
if (riid == IID_IUnknown) {
pv = (LPVOID)(LPUNKNOWN) this;
++m_cRef;
hr = ResultFromScode(S_OK);
} else {
pv = NULL;
hr = ResultFromScode(E_NOINTERFACE);
}
*ppvObj = pv;
return hr;
}
//
// CBindingFileStreamNotify::IUnknown::AddRef
//
STDMETHODIMP_(ULONG)
CBindingFileStreamNotify::AddRef(void)
{
return ++m_cRef;
}
//
// CBindingFileStreamNotify::IUnknown::Release
//
STDMETHODIMP_(ULONG)
CBindingFileStreamNotify::Release(void)
{
if (--m_cRef != 0)
return m_cRef;
delete this;
return 0;
}
//
// CBindingFileStreamNotify::OnNewStream
//
NPError
CBindingFileStreamNotify::OnNewStream(NPMIMEType type, NPStream *stream, NPBool
seekable, uint16 *stype)
{
#pragma unused (type, stream, seekable)
// REVIEW: We're not sending any notifications to the client, but there's
// not much useful information that we can provide. The client initiated
// the download, so they probably don't want to abort at the very start of
// the process. The next notification we get back from the browser is the
// filename of the data, so we there's nothing to abort.
*stype = NP_ASFILE;
return NPERR_NO_ERROR;
}
//
// CBindingFileStreamNotify::OnStreamAsFile
//
void
CBindingFileStreamNotify::OnStreamAsFile(NPStream *stream, const char* fname)
{
#pragma unused (stream)
HRESULT hr = S_OK;
if (fname != NULL)
{
// Note that we pass ourselves as the IUnknown that will release this
// resource. Default behavior for the TYMED_FILENAME type is to
// delete the file, but we want to keep it around in the cache. Plus,
// we may want to do something more in the future. The filename is
// always released by the client (see ReleaseStgMedium).
mStorageMedium.tymed = mBindType;
mStorageMedium.pUnkForRelease = (LPUNKNOWN) this;
if(mBindType == TYMED_FILE)
{
mStorageMedium.lpszFileName = OleStrdup(fname);
if(mStorageMedium.lpszFileName == NULL)
hr = E_OUTOFMEMORY;
}
else if(mBindType == TYMED_FSP)
{
mStorageMedium.pFSSpec = (FSSpec*)CoTaskMemAlloc(sizeof(FSSpec));
if(mStorageMedium.pFSSpec == NULL)
hr = E_OUTOFMEMORY;
else
{
Str255 FileName;
FileName[0] = strlen(fname);
strncpy((char*)&FileName[1], fname, FileName[0]);
FSMakeFSSpec(0, 0, FileName, mStorageMedium.pFSSpec);
}
}
if (hr == S_OK)
{
++m_cRef; // Account for 'mStorageMedium' reference
m_pBinding->m_pbsc->OnDataAvailable(BSCF_LASTDATANOTIFICATION, 0,
&g_NullFormatEtc, &mStorageMedium);
}
} else {
hr = E_FAIL;
}
// Notify the callback that the binding has stopped. Plus, we're through
// with the binding object, so we're ready to throw it away. The client
// is supposed to release the pointer in the OnStopBinding method, so we'll
// make sure we don't leak anything by just deleting the binding.
m_pBinding->m_pbsc->OnStopBinding(hr, NULL);
delete m_pBinding;
m_pBinding = NULL;
}
//
// CBindingFileStreamNotify::OnDestroyStream
//
NPError
CBindingFileStreamNotify::OnDestroyStream(NPStream *stream, NPError reason)
{
#pragma unused (stream, reason)
// The client may still have a reference to our object from the STGMEDIUM
// structure.
this->Release();
return NPERR_NO_ERROR;
}